#ifdef WIN32
#ifdef _DEBUG
// turn off warnings that say they debugging identifier has been truncated
// this warning comes up when using some STL containers
#pragma warning( disable : 4786 )
#endif
#endif

#include "MeshSet.hpp"
#include "AEntityFactory.hpp"

namespace moab
{

/*****************************************************************************************
 *                          Helper Function Declarations                                 *
 *****************************************************************************************/

/**\brief Insert into parent/child list */
static inline MeshSet::Count insert_in_vector( const MeshSet::Count count,
                                               MeshSet::CompactList& list,
                                               const EntityHandle h,
                                               int& result );

/**\brief Remvoe from parent/child list */
static inline MeshSet::Count remove_from_vector( const MeshSet::Count count,
                                                 MeshSet::CompactList& list,
                                                 const EntityHandle h,
                                                 int& result );

/**\brief Resize MeshSet::CompactList.  Returns pointer to storage */
static EntityHandle* resize_compact_list( MeshSet::Count& count, MeshSet::CompactList& clist, size_t new_list_size );
/**\brief Methods to insert/remove range-based data from contents list.
 *        Templatized to operate on both Range and set-based MeshSets.
 */
template < typename pair_iter_t >
class range_tool
{
  public:
    /** Insert range-based data into range-based MeshSet */
    inline static ErrorCode ranged_insert_entities( MeshSet::Count& count,
                                                    MeshSet::CompactList& clist,
                                                    pair_iter_t begin,
                                                    pair_iter_t end,
                                                    EntityHandle my_handle,
                                                    AEntityFactory* adj );

    /** Remove range-based data from range-based MeshSet */
    inline static ErrorCode ranged_remove_entities( MeshSet::Count& count,
                                                    MeshSet::CompactList& clist,
                                                    pair_iter_t begin,
                                                    pair_iter_t end,
                                                    EntityHandle my_handle,
                                                    AEntityFactory* adj );

    /** Insert range-based data into list-based MeshSet */
    inline static ErrorCode vector_insert_entities( MeshSet::Count& count,
                                                    MeshSet::CompactList& clist,
                                                    pair_iter_t begin,
                                                    pair_iter_t end,
                                                    EntityHandle my_handle,
                                                    AEntityFactory* adj );
};

/** Remove Range of handles fromr vector-based MeshSet */
static ErrorCode vector_remove_range( MeshSet::Count& count,
                                      MeshSet::CompactList& clist,
                                      const Range& range,
                                      EntityHandle my_handle,
                                      AEntityFactory* adj );

/** Remove range-based MeshSet contents from vector-based MeshSet */
static ErrorCode vector_remove_ranges( MeshSet::Count& count,
                                       MeshSet::CompactList& clist,
                                       const EntityHandle* pair_list,
                                       size_t num_pairs,
                                       EntityHandle my_handle,
                                       AEntityFactory* adj );

/** Remove unsorted array of handles from vector-based MeshSet */
static ErrorCode vector_remove_vector( MeshSet::Count& count,
                                       MeshSet::CompactList& clist,
                                       const EntityHandle* vect,
                                       size_t vect_size,
                                       EntityHandle my_handle,
                                       AEntityFactory* adj );

/** Insert unsorted array of handles into vector-based MeshSet */
static ErrorCode vector_insert_vector( MeshSet::Count& count,
                                       MeshSet::CompactList& clist,
                                       const EntityHandle* vect,
                                       size_t vect_size,
                                       EntityHandle my_handle,
                                       AEntityFactory* adj );

/** Convert unsorted array of handles into array of ranged [begin,end] pairs */
static void convert_to_ranges( const EntityHandle* vect_in, size_t vect_in_len, std::vector< EntityHandle >& vect_out );

/*****************************************************************************************
 *                             Parent/Child Operations                                   *
 *****************************************************************************************/

static inline MeshSet::Count insert_in_vector( const MeshSet::Count count,
                                               MeshSet::CompactList& list,
                                               const EntityHandle h,
                                               int& result )
{
    switch( count )
    {
        case MeshSet::ZERO:
            list.hnd[0] = h;
            result      = true;
            return MeshSet::ONE;
        case MeshSet::ONE:
            if( list.hnd[0] == h )
            {
                result = false;
                return MeshSet::ONE;
            }
            else
            {
                result      = true;
                list.hnd[1] = h;
                return MeshSet::TWO;
            }
        case MeshSet::TWO:
            if( list.hnd[0] == h || list.hnd[1] == h )
            {
                result = false;
                return MeshSet::TWO;
            }
            else
            {
                EntityHandle* ptr = (EntityHandle*)malloc( 3 * sizeof( EntityHandle ) );
                ptr[0]            = list.hnd[0];
                ptr[1]            = list.hnd[1];
                ptr[2]            = h;
                list.ptr[0]       = ptr;
                list.ptr[1]       = ptr + 3;
                result            = true;
                return MeshSet::MANY;
            }
        case MeshSet::MANY:
            if( std::find( list.ptr[0], list.ptr[1], h ) != list.ptr[1] )
            {
                result = false;
            }
            else
            {
                int size          = list.ptr[1] - list.ptr[0];
                list.ptr[0]       = (EntityHandle*)realloc( list.ptr[0], ( size + 1 ) * sizeof( EntityHandle ) );
                list.ptr[0][size] = h;
                list.ptr[1]       = list.ptr[0] + size + 1;
                result            = true;
            }
            return MeshSet::MANY;
    }

    return MeshSet::ZERO;
}

static inline MeshSet::Count remove_from_vector( const MeshSet::Count count,
                                                 MeshSet::CompactList& list,
                                                 const EntityHandle h,
                                                 int& result )
{
    switch( count )
    {
        case MeshSet::ZERO:
            result = false;
            return MeshSet::ZERO;
        case MeshSet::ONE:
            if( h == list.hnd[0] )
            {
                result = true;
                return MeshSet::ZERO;
            }
            else
            {
                result = false;
                return MeshSet::ONE;
            }
        case MeshSet::TWO:
            if( h == list.hnd[0] )
            {
                list.hnd[0] = list.hnd[1];
                result      = true;
                return MeshSet::ONE;
            }
            else if( h == list.hnd[1] )
            {
                result = true;
                return MeshSet::ONE;
            }
            else
            {
                result = false;
                return MeshSet::TWO;
            }
        case MeshSet::MANY: {
            EntityHandle *i, *j, *p;
            i = std::find( list.ptr[0], list.ptr[1], h );
            if( i == list.ptr[1] )
            {
                result = false;
                return MeshSet::MANY;
            }

            result = true;
            p      = list.ptr[1] - 1;
            while( i != p )
            {
                j  = i + 1;
                *i = *j;
                i  = j;
            }
            int size = p - list.ptr[0];
            if( size == 2 )
            {
                p           = list.ptr[0];
                list.hnd[0] = p[0];
                list.hnd[1] = p[1];
                free( p );
                return MeshSet::TWO;
            }
            else
            {
                list.ptr[0] = (EntityHandle*)realloc( list.ptr[0], size * sizeof( EntityHandle ) );
                list.ptr[1] = list.ptr[0] + size;
                return MeshSet::MANY;
            }
        }
    }

    return MeshSet::ZERO;
}

int MeshSet::add_parent( EntityHandle parent )
{
    int result   = 0;
    mParentCount = insert_in_vector( (Count)mParentCount, parentMeshSets, parent, result );
    return result;
}
int MeshSet::add_child( EntityHandle child )
{
    int result  = 0;
    mChildCount = insert_in_vector( (Count)mChildCount, childMeshSets, child, result );
    return result;
}

int MeshSet::remove_parent( EntityHandle parent )
{
    int result   = 0;
    mParentCount = remove_from_vector( (Count)mParentCount, parentMeshSets, parent, result );
    return result;
}
int MeshSet::remove_child( EntityHandle child )
{
    int result  = 0;
    mChildCount = remove_from_vector( (Count)mChildCount, childMeshSets, child, result );
    return result;
}

/*****************************************************************************************
 *                          Flag Conversion Operations                                   *
 *****************************************************************************************/

ErrorCode MeshSet::convert( unsigned flg, EntityHandle my_handle, AEntityFactory* adj )
{
    ErrorCode rval = MB_SUCCESS;
    if( ( mFlags & MESHSET_TRACK_OWNER ) && !( flg & MESHSET_TRACK_OWNER ) )
        rval = remove_adjacencies( my_handle, adj );
    else if( !( mFlags & MESHSET_TRACK_OWNER ) && ( flg & MESHSET_TRACK_OWNER ) )
        rval = create_adjacencies( my_handle, adj );
    if( MB_SUCCESS != rval ) return rval;

    if( !( mFlags & MESHSET_ORDERED ) && ( flg & MESHSET_ORDERED ) )
    {
        size_t datalen;
        EntityHandle* data = get_contents( datalen );
        if( datalen )
        {
            std::vector< EntityHandle > list( datalen );
            memcpy( &list[0], data, datalen * sizeof( EntityHandle ) );
            int num_ents  = num_entities();
            Count count   = (Count)mContentCount;
            data          = resize_compact_list( count, contentList, num_ents );
            mContentCount = count;
            assert( list.size() % 2 == 0 );
            std::vector< EntityHandle >::iterator i = list.begin();
            while( i != list.end() )
            {
                EntityHandle h = *i;
                ++i;
                EntityHandle e = *i;
                ++i;
                for( ; h <= e; ++h )
                {
                    *data = h;
                    ++data;
                }
            }
        }
    }
    else if( ( mFlags & MESHSET_ORDERED ) && !( flg & MESHSET_ORDERED ) )
    {
        size_t datalen;
        EntityHandle* data = get_contents( datalen );
        if( datalen )
        {
            std::vector< EntityHandle > ranges;
            convert_to_ranges( data, datalen, ranges );
            Count count   = (Count)mContentCount;
            data          = resize_compact_list( count, contentList, ranges.size() );
            mContentCount = count;
            memcpy( data, &ranges[0], ranges.size() * sizeof( EntityHandle ) );
        }
    }

    return MB_SUCCESS;
}

ErrorCode MeshSet::create_adjacencies( EntityHandle my_handle, AEntityFactory* adj )
{
    ErrorCode rval = MB_SUCCESS;
    ;
    size_t count;
    const EntityHandle* const ptr = get_contents( count );
    const EntityHandle* const end = ptr + count;
    if( vector_based() )
    {
        for( const EntityHandle* i = ptr; i != end; ++i )
        {
            rval = adj->add_adjacency( *i, my_handle, false );
            if( MB_SUCCESS != rval )
            {
                for( const EntityHandle* j = ptr; j != i; ++j )
                    adj->remove_adjacency( *j, my_handle );
                return rval;
            }
        }
    }
    else
    {
        assert( 0 == count % 2 );
        for( const EntityHandle* i = ptr; i != end; i += 2 )
        {
            for( EntityHandle h = i[0]; h <= i[1]; ++h )
            {
                rval = adj->add_adjacency( h, my_handle, false );
                if( MB_SUCCESS != rval )
                {
                    for( EntityHandle j = i[0]; j < h; ++j )
                        adj->remove_adjacency( j, my_handle );
                    for( const EntityHandle* j = ptr; j != i; j += 2 )
                        for( EntityHandle k = j[0]; k <= j[1]; ++k )
                            adj->remove_adjacency( k, my_handle );
                    return rval;
                }
            }
        }
    }
    return MB_SUCCESS;
}

ErrorCode MeshSet::remove_adjacencies( EntityHandle my_handle, AEntityFactory* adj )
{
    size_t count;
    const EntityHandle* const ptr = get_contents( count );
    const EntityHandle* const end = ptr + count;
    if( vector_based() )
    {
        for( const EntityHandle* i = ptr; i != end; ++i )
            adj->remove_adjacency( *i, my_handle );
    }
    else
    {
        assert( 0 == count % 2 );
        for( const EntityHandle* i = ptr; i != end; i += 2 )
            for( EntityHandle h = i[0]; h <= i[1]; ++h )
                adj->remove_adjacency( h, my_handle );
    }
    return MB_SUCCESS;
}

/*****************************************************************************************
 *                          Contents Modifiction Methods                                 *
 *****************************************************************************************/

static EntityHandle* resize_compact_list( MeshSet::Count& count, MeshSet::CompactList& clist, size_t new_list_size )
{
    if( count <= 2 )
    {
        if( new_list_size <= 2 )
        {
            count = (MeshSet::Count)new_list_size;
            return clist.hnd;
        }
        else
        {
            EntityHandle* list = (EntityHandle*)malloc( new_list_size * sizeof( EntityHandle ) );
            list[0]            = clist.hnd[0];
            list[1]            = clist.hnd[1];
            clist.ptr[0]       = list;
            clist.ptr[1]       = list + new_list_size;
            count              = MeshSet::MANY;
            return list;
        }
    }
    else if( new_list_size > 2 )
    {
        if( new_list_size > (size_t)( clist.ptr[1] - clist.ptr[0] ) )
            clist.ptr[0] = (EntityHandle*)realloc( clist.ptr[0], new_list_size * sizeof( EntityHandle ) );
        clist.ptr[1] = clist.ptr[0] + new_list_size;
        count        = MeshSet::MANY;
        return clist.ptr[0];
    }
    else
    {
        EntityHandle* list = clist.ptr[0];
        clist.hnd[0]       = list[0];
        clist.hnd[1]       = list[1];
        free( list );
        count = (MeshSet::Count)new_list_size;
        return clist.hnd;
    }
}

typedef std::pair< EntityHandle, EntityHandle > MeshSetRange;

class MeshSetRComp
{
  public:
    bool operator()( const MeshSetRange& r, const MeshSetRange& h )
    {
        return r.second < h.first;
    }
};

template < typename pair_iter_t >
inline ErrorCode range_tool< pair_iter_t >::ranged_insert_entities( MeshSet::Count& count,
                                                                    MeshSet::CompactList& clist,
                                                                    pair_iter_t begin,
                                                                    pair_iter_t end,
                                                                    EntityHandle my_handle,
                                                                    AEntityFactory* adj )
{
    // first pass:
    // 1) merge existing ranges
    // 2) count number of new ranges that must be inserted
    EntityHandle* list_ptr;
    size_t list_size;
    if( count < MeshSet::MANY )
    {
        list_ptr  = clist.hnd;
        list_size = count;
    }
    else
    {
        list_ptr  = clist.ptr[0];
        list_size = clist.ptr[1] - clist.ptr[0];
    }

    MeshSetRange* list = reinterpret_cast< MeshSetRange* >( list_ptr );
    assert( 0 == list_size % 2 );
    assert( 2 * sizeof( EntityHandle ) == sizeof( MeshSetRange ) );
    list_size /= 2;
    MeshSetRange* const list_end = list + list_size;
    MeshSetRange *list_read = list, *list_write = list;
    pair_iter_t i = begin;

    // count number of range pairs that are straight insertions
    // (don't overlap any range pair in the current set) that
    // could not be inserted during the first pass.
    size_t insert_count = 0;

    // merge lists
    while( i != end )
    {
        // find first range that intersects the current input range

        // do binary search if no holes in current set contents
        if( list_read == list_write )
        {
            // subtract one from i->first because if it is one greater
            // then the the last value of some block, then we want that
            // block to append to.
            MeshSetRange tmp;
            tmp.first  = i->first - 1;
            tmp.second = i->second;
            list_write = std::lower_bound( list_read, list_end, tmp, MeshSetRComp() );
            list_read  = list_write;
        }
        // otherwise shift down until we find where we find a range block
        // that intersects
        else
            while( list_read != list_end && list_read->second + 1 < i->first )
            {
                *list_write = *list_read;
                ++list_write;
                ++list_read;
            }

        // handle any straight insertions of range blocks
        for( ; i != end && ( list_read == list_end || i->second + 1 < list_read->first ); ++i )
        {
            // If we haven't removed any range pairs, we don't have space to
            // insert here.  Defer the insertion until later.
            if( list_read == list_write )
            {
                ++insert_count;
            }
            else
            {
                if( adj )
                    for( EntityHandle j = i->first; j <= i->second; ++j )
                        adj->add_adjacency( j, my_handle, false );

                list_write->first  = i->first;
                list_write->second = i->second;
                ++list_write;
            }
        }

        // merge all the stuff that gets merged into a single range pair
        // from both the input list and the existing set data
        if( list_read != list_end )
        {
            MeshSetRange working = *list_read;  // copy because might be the same as list_write
            ++list_read;

            // Check if we need to prepend to the existing block.
            // We only need to check this for the first input range because
            // after this working.first will always be the first possible handle
            // in the merged set of ranges.
            if( i != end && i->first < working.first && i->second + 1 >= working.first )
            {
                if( adj )
                    for( EntityHandle h = i->first; h < working.first; ++h )
                        adj->add_adjacency( h, my_handle, false );
                working.first = i->first;
            }

            // Now append from the input list and the remaining set contents
            // until we've consolidated all overlapping/touching ranges.
            bool done = false;
            while( !done )
            {
                // does next set contents range touch working range?
                bool set_overlap = list_read != list_end && list_read->first <= working.second + 1;
                // does next input range touch working range?
                bool inp_overlap = i != end && i->first <= working.second + 1;

                // if both ranges touch...
                if( inp_overlap && set_overlap )
                {
                    // if next set range is contained in working, skip it
                    if( list_read->second <= working.second ) ++list_read;
                    // if next input range is contained in working, skip it
                    else if( i->second <= working.second )
                        ++i;
                    // Otherwise set the working end to the smaller of the two
                    // ends: either the next set end or the next input end.
                    // We want the smaller of the two because the larger might
                    // intersect additional ranges in the other list.
                    else if( list_read->second <= i->second )
                    {
                        working.second = list_read->second;
                        ++list_read;
                    }
                    else
                    {
                        working.second = i->second;
                        ++i;
                    }
                }
                // If only the input range intersect the current working range...
                else if( inp_overlap )
                {
                    // Would it be more efficient to just extent 'working' to
                    // the end of the current input range?  We'd end up adding
                    // adjacencies for for entities that are already in the tracking
                    // set and therefore already have the adjacency.
                    EntityHandle last = i->second;
                    if( list_read != list_end && list_read->first < last )
                        last = list_read->first - 1;
                    else
                        ++i;

                    if( last > working.second )
                    {
                        if( adj )
                            for( EntityHandle h = working.second + 1; h <= last; ++h )
                                adj->add_adjacency( h, my_handle, false );

                        working.second = last;
                    }
                }
                else if( set_overlap )
                {
                    if( working.second < list_read->second ) working.second = list_read->second;
                    ++list_read;
                }
                else
                {
                    done = true;
                }
            }

            assert( list_write < list_read );
            *list_write = working;
            ++list_write;
        }
    }

    // shuffle down entries to fill holes
    if( list_read == list_write )
        list_read = list_write = list_end;
    else
        while( list_read < list_end )
        {
            *list_write = *list_read;
            ++list_read;
            ++list_write;
        }

    // adjust allocated array size
    const size_t occupied_size = list_write - list;
    const size_t new_list_size = occupied_size + insert_count;
    list_ptr                   = resize_compact_list( count, clist, 2 * new_list_size );
    // done?
    if( !insert_count ) return MB_SUCCESS;
    list = reinterpret_cast< MeshSetRange* >( list_ptr );

    // Second pass: insert non-mergable range pairs
    // All range pairs in the input are either completely disjoint from
    // the ones in the mesh set and must be inserted or are entirely contained
    // within a range pair in the mesh set.
    assert( begin != end );  // can't have items to insert if given empty input list
    pair_iter_t ri = end;
    --ri;
    list_write = list + new_list_size - 1;
    list_read  = list + occupied_size - 1;
    for( ; list_write >= list; --list_write )
    {
        if( list_read >= list )
        {
            while( ri->first >= list_read->first && ri->second <= list_read->second )
            {
                assert( ri != begin );
                --ri;
            }

            if( list_read->first > ri->second )
            {
                *list_write = *list_read;
                --list_read;
                continue;
            }
        }

        assert( insert_count > 0 );
        if( adj )
            for( EntityHandle h = ri->first; h <= ri->second; ++h )
                adj->add_adjacency( h, my_handle, false );
        list_write->first  = ri->first;
        list_write->second = ri->second;

        // don't have reverse iterator, so check before decrement
        // if insert_count isn't zero, must be more in range
        if( 0 == --insert_count )
        {
            assert( list_read == list_write - 1 );
            break;
        }
        else
        {
            --ri;
        }
    }

    assert( !insert_count );
    return MB_SUCCESS;
}

template < typename pair_iter_t >
inline ErrorCode range_tool< pair_iter_t >::ranged_remove_entities( MeshSet::Count& count,
                                                                    MeshSet::CompactList& clist,
                                                                    pair_iter_t begin,
                                                                    pair_iter_t end,
                                                                    EntityHandle my_handle,
                                                                    AEntityFactory* adj )
{
    // first pass:
    // 1) remove (from) existing ranges
    // 2) count number of ranges that must be split
    ptrdiff_t split_count = 0;
    EntityHandle* list;
    size_t list_size;
    if( count < MeshSet::MANY )
    {
        list      = clist.hnd;
        list_size = count;
    }
    else
    {
        list      = clist.ptr[0];
        list_size = clist.ptr[1] - clist.ptr[0];
    }

    EntityHandle* list_write     = list;
    EntityHandle *const list_end = list + list_size, *list_read = list;
    pair_iter_t i = begin;

    while( list_read != list_end && i != end )
    {

        while( i != end && i->second < list_read[0] )
            ++i;
        if( i == end ) break;

        // if there are holes in the current array, shuffle blocks
        // down until we find the next block to remove
        if( list_read != list_write )
        {
            while( list_read != list_end && i->second < list_read[0] )
            {
                list_write[0] = list_read[0];
                list_write[1] = list_read[1];
                list_write += 2;
                list_read += 2;
            }
        }
        // otherwise do a binary search
        else
        {
            list_write = std::lower_bound( list_write, list_end, i->first );
            // if in middle of range block (odd index), back up to start of block
            list_write -= ( list_write - list ) % 2;
            list_read = list_write;
        }

        // if everything remaning is past end of set contents...
        if( list_read == list_end ) break;

        // skip any remove pairs that aren't in the list
        if( i->second < list_read[0] )
        {
            ++i;
            continue;
        }

        // Begin by assuming that we will keep the entire block
        list_write[0] = list_read[0];
        list_write[1] = list_read[1];
        list_read += 2;

        for( ; i != end && i->first <= list_write[1]; ++i )
        {
            if( i->first <= list_write[0] )
            {
                // remove whole block
                if( i->second >= list_write[1] )
                {
                    if( adj )
                        for( EntityHandle h = list_write[0]; h <= list_write[1]; ++h )
                            adj->remove_adjacency( h, my_handle );
                    list_write -= 2;
                    break;
                }
                // remove from start of block
                else if( i->second >= list_write[0] )
                {
                    if( adj )
                        for( EntityHandle h = list_write[0]; h <= i->second; ++h )
                            adj->remove_adjacency( h, my_handle );
                    list_write[0] = i->second + 1;
                }
            }
            else if( i->first <= list_write[1] )
            {
                // remove from end of block
                if( i->second >= list_write[1] )
                {
                    if( adj )
                        for( EntityHandle h = i->first; h <= list_write[1]; ++h )
                            adj->remove_adjacency( h, my_handle );
                    list_write[1] = i->first - 1;
                    // list_write += 2;
                    break;
                }
                // split block
                else
                {
                    if( adj )
                        for( EntityHandle h = i->first; h <= i->second; ++h )
                            adj->remove_adjacency( h, my_handle );

                    if( list_read - list_write <= 2 )
                    {
                        ++split_count;
                        continue;
                    }
                    else
                    {
                        list_write[3] = list_write[1];
                        list_write[1] = i->first - 1;
                        list_write[2] = i->second + 1;
                        list_write += 2;
                    }
                }
            }
        }
        list_write += 2;
    }

    // shuffle down entries to fill holes
    if( list_read == list_write )
        list_read = list_write = list_end;
    else
        while( list_read < list_end )
        {
            list_write[0] = list_read[0];
            list_write[1] = list_read[1];
            list_read += 2;
            list_write += 2;
        }

    // adjust allocated array size
    const size_t occupied_size = list_write - list;
    const size_t new_list_size = occupied_size + 2 * split_count;
    list                       = resize_compact_list( count, clist, new_list_size );
    // done?
    if( !split_count ) return MB_SUCCESS;

    // Second pass: split range pairs
    // All range pairs in the input are either already removed or
    // require one of the existing range pairs to be split
    assert( begin != end );  // can't have ranges to split if given empty input list
    pair_iter_t ri = end;
    --ri;
    list_write = list + new_list_size - 2;
    list_read  = list + occupied_size - 2;
    for( ; list_write >= list; list_write -= 2 )
    {
        if( list_read >= list )
        {
            while( ri->second > list_read[1] )
            {
                assert( ri != begin );
                --ri;
            }

            if( list_read[0] > ri->second )
            {
                list_write[0] = list_read[0];
                list_write[1] = list_read[1];
                list_read -= 2;
                continue;
            }
        }

        assert( split_count > 0 );
        list_write[0] = ri->second + 1;
        list_write[1] = list_read[1];
        list_read[1]  = ri->first - 1;

        // don't have reverse iterator, so check before decrement
        // if insert_count isn't zero, must be more in range
        if( 0 == --split_count )
        {
            assert( list_read == list_write - 2 );
            break;
        }
        else
        {
            --ri;
        }
    }

    assert( !split_count );
    return MB_SUCCESS;
}

template < typename pair_iter_t >
inline ErrorCode range_tool< pair_iter_t >::vector_insert_entities( MeshSet::Count& count,
                                                                    MeshSet::CompactList& clist,
                                                                    pair_iter_t begin,
                                                                    pair_iter_t end,
                                                                    EntityHandle my_handle,
                                                                    AEntityFactory* adj )
{
    const size_t init_size = count < MeshSet::MANY ? (int)count : clist.ptr[1] - clist.ptr[0];
    size_t add_size        = 0;
    for( pair_iter_t i = begin; i != end; ++i )
        add_size += i->second - i->first + 1;
    EntityHandle* list = resize_compact_list( count, clist, init_size + add_size );
    EntityHandle* li   = list + init_size;

    for( pair_iter_t i = begin; i != end; ++i )
    {
        for( EntityHandle h = i->first; h <= i->second; ++h )
        {
            if( adj ) adj->add_adjacency( h, my_handle, false );
            *li = h;
            ++li;
        }
    }

    return MB_SUCCESS;
}

static ErrorCode vector_remove_range( MeshSet::Count& count,
                                      MeshSet::CompactList& clist,
                                      const Range& range,
                                      EntityHandle my_handle,
                                      AEntityFactory* adj )
{
    EntityHandle* list;
    size_t list_size;
    if( count < MeshSet::MANY )
    {
        list      = clist.hnd;
        list_size = count;
    }
    else
    {
        list      = clist.ptr[0];
        list_size = clist.ptr[1] - clist.ptr[0];
    }

    const EntityHandle* const list_end = list + list_size;
    EntityHandle* list_write           = list;
    for( const EntityHandle* list_read = list; list_read != list_end; ++list_read )
    {
        if( range.find( *list_read ) == range.end() )
        {  // keep
            *list_write = *list_read;
            ++list_write;
        }
        else if( adj )
        {
            adj->remove_adjacency( *list_read, my_handle );
        }
    }

    resize_compact_list( count, clist, list_write - list );
    return MB_SUCCESS;
}

static ErrorCode vector_remove_ranges( MeshSet::Count& count,
                                       MeshSet::CompactList& clist,
                                       const EntityHandle* pair_list,
                                       size_t num_pairs,
                                       EntityHandle my_handle,
                                       AEntityFactory* adj )
{
    EntityHandle* list;
    size_t list_size;
    if( count < MeshSet::MANY )
    {
        list      = clist.hnd;
        list_size = count;
    }
    else
    {
        list      = clist.ptr[0];
        list_size = clist.ptr[1] - clist.ptr[0];
    }

    const EntityHandle *const list_end = list + list_size, *const input_end = pair_list + 2 * num_pairs;
    EntityHandle* list_write = list;
    for( const EntityHandle* list_read = list; list_read != list_end; ++list_read )
    {
        const EntityHandle* ptr = std::lower_bound( pair_list, input_end, *list_read );
        if( ( ptr != input_end && ( *ptr == *list_read || ( ptr - pair_list ) % 2 ) ) &&  // if in delete list
            std::find( list_read + 1, list_end, *list_read ) == list_end )
        {  // and is last occurance in list
            // only remove adj if no previous occurance
            if( adj && std::find( list, list_write, *list_read ) == list_write )
                adj->remove_adjacency( *list_read, my_handle );
        }
        else
        {
            *list_write = *list_read;
            ++list_write;
        }
    }

    resize_compact_list( count, clist, list_write - list );
    return MB_SUCCESS;
}

static ErrorCode vector_remove_vector( MeshSet::Count& count,
                                       MeshSet::CompactList& clist,
                                       const EntityHandle* vect,
                                       size_t vect_size,
                                       EntityHandle my_handle,
                                       AEntityFactory* adj )
{
    EntityHandle* list;
    size_t list_size;
    if( count < MeshSet::MANY )
    {
        list      = clist.hnd;
        list_size = count;
    }
    else
    {
        list      = clist.ptr[0];
        list_size = clist.ptr[1] - clist.ptr[0];
    }

    const EntityHandle *const list_end = list + list_size, *const input_end = vect + vect_size;
    EntityHandle* list_write = list;
    for( const EntityHandle* list_read = list; list_read != list_end; ++list_read )
    {
        if( std::find( vect, input_end, *list_read ) != input_end &&  // if in delete list
            std::find( list_read + 1, list_end, *list_read ) == list_end )
        {  // and is last occurance in list
            // only remove adj if no previous occurance?
            if( adj )  // && std::find(list, list_write, *list_read) == list_write)
                adj->remove_adjacency( *list_read, my_handle );
        }
        else
        {
            *list_write = *list_read;
            ++list_write;
        }
    }

    resize_compact_list( count, clist, list_write - list );
    return MB_SUCCESS;
}

static ErrorCode vector_insert_vector( MeshSet::Count& count,
                                       MeshSet::CompactList& clist,
                                       const EntityHandle* vect,
                                       size_t vect_size,
                                       EntityHandle my_handle,
                                       AEntityFactory* adj )
{
    const size_t orig_size = count < MeshSet::MANY ? (int)count : clist.ptr[1] - clist.ptr[0];
    EntityHandle* list     = resize_compact_list( count, clist, orig_size + vect_size );
    if( adj )
        for( size_t i = 0; i < vect_size; ++i )
            adj->add_adjacency( vect[i], my_handle, false );
    memcpy( list + orig_size, vect, sizeof( EntityHandle ) * vect_size );
    return MB_SUCCESS;
}

ErrorCode MeshSet::insert_entity_ranges( const EntityHandle* range_vect,
                                         size_t len,
                                         EntityHandle my_h,
                                         AEntityFactory* adj )
{
    typedef const std::pair< EntityHandle, EntityHandle >* pair_vect_t;
    pair_vect_t pair_vect = reinterpret_cast< pair_vect_t >( range_vect );
    MeshSet::Count count  = static_cast< MeshSet::Count >( mContentCount );
    ErrorCode rval;
    if( !vector_based() )
        rval = range_tool< pair_vect_t >::ranged_insert_entities( count, contentList, pair_vect, pair_vect + len / 2,
                                                                  my_h, tracking() ? adj : 0 );
    else
        rval = range_tool< pair_vect_t >::vector_insert_entities( count, contentList, pair_vect, pair_vect + len / 2,
                                                                  my_h, tracking() ? adj : 0 );
    mContentCount = count;
    return rval;
}

ErrorCode MeshSet::insert_entity_ranges( const Range& range, EntityHandle my_h, AEntityFactory* adj )
{
    ErrorCode rval;
    MeshSet::Count count = static_cast< MeshSet::Count >( mContentCount );
    if( !vector_based() )
        rval = range_tool< Range::const_pair_iterator >::ranged_insert_entities(
            count, contentList, range.const_pair_begin(), range.const_pair_end(), my_h, tracking() ? adj : 0 );
    else
        rval = range_tool< Range::const_pair_iterator >::vector_insert_entities(
            count, contentList, range.const_pair_begin(), range.const_pair_end(), my_h, tracking() ? adj : 0 );
    mContentCount = count;
    return rval;
}

ErrorCode MeshSet::remove_entity_ranges( const EntityHandle* range_vect,
                                         size_t len,
                                         EntityHandle my_h,
                                         AEntityFactory* adj )
{
    ErrorCode rval;
    MeshSet::Count count = static_cast< MeshSet::Count >( mContentCount );
    if( vector_based() )
        rval = vector_remove_ranges( count, contentList, range_vect, len / 2, my_h, tracking() ? adj : 0 );
    else
    {
        typedef const std::pair< EntityHandle, EntityHandle >* pair_vect_t;
        pair_vect_t pair_vect = reinterpret_cast< pair_vect_t >( range_vect );
        rval = range_tool< pair_vect_t >::ranged_remove_entities( count, contentList, pair_vect, pair_vect + len / 2,
                                                                  my_h, tracking() ? adj : 0 );
    }
    mContentCount = count;
    return rval;
}

ErrorCode MeshSet::remove_entity_ranges( const Range& range, EntityHandle my_h, AEntityFactory* adj )
{
    ErrorCode rval;
    MeshSet::Count count = static_cast< MeshSet::Count >( mContentCount );
    if( vector_based() )
        rval = vector_remove_range( count, contentList, range, my_h, tracking() ? adj : 0 );
    else
        rval = range_tool< Range::const_pair_iterator >::ranged_remove_entities(
            count, contentList, range.const_pair_begin(), range.const_pair_end(), my_h, tracking() ? adj : 0 );
    mContentCount = count;
    return rval;
}

ErrorCode MeshSet::intersect( const MeshSet* other, EntityHandle my_handle, AEntityFactory* adj )
{
    ErrorCode rval;
    if( !vector_based() && !other->vector_based() )
    {
        size_t other_count             = 0;
        const EntityHandle* other_vect = other->get_contents( other_count );
        if( !other_count ) return clear( my_handle, adj );
        assert( 0 == other_count % 2 );

        std::vector< EntityHandle > compliment;
        compliment.reserve( other_count + 4 );
        if( *other_vect > 0 )
        {
            compliment.push_back( 0 );
            compliment.push_back( *other_vect - 1 );
        }
        ++other_vect;
        const EntityHandle* const other_end = other_vect + other_count - 2;
        for( ; other_vect < other_end; other_vect += 2 )
        {
            compliment.push_back( other_vect[0] + 1 );
            compliment.push_back( other_vect[1] - 1 );
        }
        if( *other_vect < ~(EntityHandle)0 )
        {
            compliment.push_back( *other_vect + 1 );
            compliment.push_back( ~(EntityHandle)0 );
        }

        return remove_entity_ranges( &compliment[0], compliment.size(), my_handle, adj );
    }
    else
    {
        Range my_ents, other_ents;
        rval = get_entities( my_ents );
        if( MB_SUCCESS != rval ) return rval;
        rval = other->get_entities( other_ents );
        return remove_entities( moab::subtract( my_ents, other_ents ), my_handle, adj );
    }
}

static void convert_to_ranges( const EntityHandle* vect_in, size_t vect_in_len, std::vector< EntityHandle >& vect_out )
{
    vect_out.reserve( 2 * vect_in_len );
    vect_out.resize( vect_in_len );
    std::copy( vect_in, vect_in + vect_in_len, vect_out.begin() );
    std::sort( vect_out.begin(), vect_out.end() );
    vect_out.erase( std::unique( vect_out.begin(), vect_out.end() ), vect_out.end() );

    // duplicate all entries
    vect_out.resize( vect_out.size() * 2 );
    for( long i = vect_out.size() - 1; i >= 0; --i )
        vect_out[i] = vect_out[i / 2];

    // compact adjacent ranges
    std::vector< EntityHandle >::iterator r = vect_out.begin(), w = vect_out.begin();
    while( r != vect_out.end() )
    {
        *w = *r;
        ++w;
        ++r;
        *w = *r;
        ++r;

        while( r != vect_out.end() && *w + 1 == *r )
        {
            ++r;
            *w = *r;
            ++r;
        }
        ++w;
    }

    // remove extra space
    vect_out.erase( w, vect_out.end() );
}

ErrorCode MeshSet::insert_entity_vector( const EntityHandle* vect, size_t len, EntityHandle my_h, AEntityFactory* adj )
{
    MeshSet::Count count = static_cast< MeshSet::Count >( mContentCount );
    ErrorCode rval;
    if( vector_based() )
        rval = vector_insert_vector( count, contentList, vect, len, my_h, tracking() ? adj : 0 );
    else
    {
        std::vector< EntityHandle > rangevect;
        convert_to_ranges( vect, len, rangevect );
        typedef const std::pair< EntityHandle, EntityHandle >* pair_vect_t;
        pair_vect_t pair_vect = ( rangevect.empty() ) ? NULL : reinterpret_cast< pair_vect_t >( &rangevect[0] );
        rval                  = range_tool< pair_vect_t >::ranged_insert_entities(
            count, contentList, pair_vect, pair_vect + rangevect.size() / 2, my_h, tracking() ? adj : 0 );
    }
    mContentCount = count;
    return rval;
}

ErrorCode MeshSet::remove_entity_vector( const EntityHandle* vect, size_t len, EntityHandle my_h, AEntityFactory* adj )
{
    MeshSet::Count count = static_cast< MeshSet::Count >( mContentCount );
    ErrorCode rval;
    if( vector_based() )
        rval = vector_remove_vector( count, contentList, vect, len, my_h, tracking() ? adj : 0 );
    else
    {
        std::vector< EntityHandle > rangevect;
        convert_to_ranges( vect, len, rangevect );
        typedef const std::pair< EntityHandle, EntityHandle >* pair_vect_t;
        pair_vect_t pair_vect = ( rangevect.empty() ) ? NULL : reinterpret_cast< pair_vect_t >( &rangevect[0] );
        rval                  = range_tool< pair_vect_t >::ranged_remove_entities(
            count, contentList, pair_vect, pair_vect + rangevect.size() / 2, my_h, tracking() ? adj : 0 );
    }
    mContentCount = count;
    return rval;
}

ErrorCode MeshSet::replace_entities( EntityHandle my_handle,
                                     const EntityHandle* old_entities,
                                     const EntityHandle* new_entities,
                                     size_t num_ents,
                                     AEntityFactory* adjfact )
{
    if( vector_based() )
    {
        ErrorCode result = MB_SUCCESS;
        size_t count;
        EntityHandle* vect           = get_contents( count );
        EntityHandle* const vect_end = vect + count;
        for( size_t i = 0; i < num_ents; ++i )
        {
            EntityHandle* p = std::find( vect, vect_end, old_entities[i] );
            if( p == vect_end )
            {
                result = MB_ENTITY_NOT_FOUND;
            }
            else
                do
                {
                    if( tracking() )
                    {
                        adjfact->remove_adjacency( *p, my_handle );
                        adjfact->add_adjacency( new_entities[i], my_handle, false );
                    }
                    *p = new_entities[i];
                    p  = std::find( p + 1, vect_end, old_entities[i] );
                } while( p != vect_end );
        }
        return result;
    }
    else
    {
        ErrorCode r1 = remove_entities( old_entities, num_ents, my_handle, adjfact );
        ErrorCode r2 = add_entities( new_entities, num_ents, my_handle, adjfact );
        return ( MB_SUCCESS == r2 ) ? r1 : r2;
    }
}

/*****************************************************************************************
 *                                  Misc. Methods                                        *
 *****************************************************************************************/

unsigned long MeshSet::get_memory_use() const
{
    unsigned long result = 0;
    if( mParentCount == MANY ) result += parentMeshSets.ptr[1] - parentMeshSets.ptr[0];
    if( mChildCount == MANY ) result += childMeshSets.ptr[1] - childMeshSets.ptr[0];
    if( mContentCount == MANY ) result += contentList.ptr[1] - contentList.ptr[0];
    return sizeof( EntityHandle ) * result;
}

}  // namespace moab
